package come.example.trigger.job;

import come.example.domain.strategy.model.vo.StrategyAwardStockKeyVO;
import come.example.domain.strategy.service.IRaffleStock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 更新奖品库存任务；为了不让更新库存的压力打到数据库中
 * 这里采用了redis更新缓存库存，异步队列更新数据库，数据库表最终一致即可
 */

/**
 * 问题背景：
 * 在高并发活动（如秒杀、抽奖）中，如果每次库存扣减都直接操作数据库，会导致数据库压力过大，甚至崩溃。
 * 解决方案：
 *     Redis 缓存库存：实时扣减 Redis 中的库存（原子操作，高性能）。
 *     异步队列更新数据库：将库存变更记录到队列，通过定时任务批量更新到数据库，减少数据库写入频率。
 *
 * 最终一致性：
 * 数据库的库存数据可能短暂落后于 Redis，但通过异步机制最终会保持一致。
 *
 * 通过定时任务获取存放在 redis 中的库存消费队列。并依照队列中的数据更新数据库。
 * 因为存放数据是延迟存放，消费也是定时任务，这样会对数据库的库表更新的压力会降低很多，不会产生大量的竞争。
 */

@Slf4j
@Component()
public class UpdateAwardStockJob {

    @Resource
    private IRaffleStock raffleStock;

    // 说明这是一个定时任务，每5秒执行一次
    @Scheduled(cron = "0/5 * * * * ?")
    /**
     * 定时任务流程：
     *     每5秒尝试从队列中获取库存变更记录。
     *     如果获取到记录，更新数据库中的库存。
     *     如果队列为空或无记录，直接跳过
     */
    public void exec(){
        try{
            log.info("定时任务，更新奖品消耗库存【延迟队列获取，降低对数据库的更新频次，不要产生竞争】");
            // 拿到队列中获取库存变更记录
            StrategyAwardStockKeyVO strategyAwardStockKeyVO = raffleStock.takeQueueValue();
            if(null == strategyAwardStockKeyVO){ return; }
            log.info("定时任务，更新奖品消耗库存 strategyId:{} awardId:{}", strategyAwardStockKeyVO.getStrategyId(), strategyAwardStockKeyVO.getAwardId());

            // 更新数据库
            raffleStock.updateStrategyAwardStock(strategyAwardStockKeyVO.getStrategyId(), strategyAwardStockKeyVO.getAwardId());
        }catch(Exception e){
            log.error("定时任务，更新奖品消耗库存失败", e);
        }
    }
}
